#include "..\pch.h"

/**
	This module is a plain C class emulation. The POC written by decoder was in cpp and used some classes
	in  particular for the local negotiator.
	See https://stackoverflow.com/questions/40992945/convert-a-cpp-class-cpp-file-into-a-c-structure-c-file
	for how I emulated a class in pure C.

	This class defines a base server object. Inheritance will be emulated in order to create a
	Rogue WinRM service and a simple http server derived from this class.
*/



/**
	Constructor of the Server class. Setup the addresses of class methods,
	and initialize some arguments

	@param		Server* this			Address of the instantiated object.
	@param		char*	listen_address	Address of IP (as string)
	@param		char*	listen_port		Address of port (as string)
	@param		BOOL	debug			TRUE or FALSE. Defined in main/dllmain
 */
void initService(Server* this, char* listen_address, char* listen_port)
{
	ZeroMemory(this, sizeof(Server));

	this->destruct = &destructService;
	this->listenerStart = &startListener;
	this->serverStop = &SocketError;
	this->listen_address = listen_address;
	this->listen_port = listen_port;
	this->socket = INVALID_SOCKET;
	this->socketInfos = NULL;

	ZeroMemory(&this->hints, sizeof(this->hints));
	this->hints.ai_family = AF_INET;
	this->hints.ai_socktype = SOCK_STREAM;
	this->hints.ai_protocol = IPPROTO_TCP;
	this->hints.ai_flags = AI_PASSIVE;

	this->listenerStart(this);

	return;
}




/**
	Destructor of the Server class. Free allocated memory.

	@param		Server* this			Address of the instantiated object.
 */
void destructService(Server* this)
{
	/*for (this->allocationMapLen=10; this->allocationMapLen > 0; this->allocationMapLen--)
	{
		free(this->allocationMap[this->allocationMapLen]);
	}*/
	return;
}



/**
	Initialization of the server :
		- Initialize Winsock
		- Resolve the server address and port
		- Create a SOCKET for connecting to server
		- Setup the TCP listening socket
		- Accept a client socket and put it as argument to make it available for derived servers.
		- Clean the listen socket

	@param		Server* this			Address of the instantiated object.
 */
static void startListener(Server* this)
{
	int iResult = -1;
	WSADATA wsaData;
	SOCKET ListenSocket = INVALID_SOCKET;
	struct addrinfo* result = NULL;

	// Initialize Winsock
	iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iResult != 0)
	{
		dprintf("[startListener] ERROR: WSAStartup failed with error: %d", iResult);
		exit(-1);
	}
    dprintf("[startListener] SUCCESS: WSAStartup initialized");

	// Resolve the server address and port
	iResult = getaddrinfo(this->listen_address, this->listen_port, &this->hints, &this->socketInfos);
	if (iResult != 0)
	{
		dprintf("[startListener] ERROR: getaddrinfo failed with error: %d\n", iResult);
		WSACleanup();
		exit(-1);
	}
    dprintf("[startListener] SUCCESS: getaddrinfo initialized. host:%s, port: %s", this->listen_address, this->listen_port);

	// Create a SOCKET for connecting to server
	ListenSocket = socket(this->socketInfos->ai_family, this->socketInfos->ai_socktype, this->socketInfos->ai_protocol);
	if (ListenSocket == INVALID_SOCKET)
	{
		dprintf("[startListener] ERROR: socket creation failed with error: %ld\n", WSAGetLastError());
		freeaddrinfo(this->socketInfos);
		WSACleanup();
		exit(-1);
	}
    dprintf("[startListener] SUCCESS: socket created.");

	// Setup the TCP listening socket
	iResult = bind(ListenSocket, this->socketInfos->ai_addr, (int)this->socketInfos->ai_addrlen);
	if (iResult == SOCKET_ERROR)
	{
		if (strcmp(this->listen_port, "5985") == 0) { dprintf("[startListener] ERROR: WinRM already running on port 5985. Unexploitable!"); }
		dprintf("[startListener] ERROR: bind failed with error: %d", WSAGetLastError());
		freeaddrinfo(this->socketInfos);
		this->serverStop(this, ListenSocket, "[startListener] ERROR: bind failed");
	}
    dprintf("[startListener] SUCCESS: socket bound.");

	freeaddrinfo(this->socketInfos);

	iResult = listen(ListenSocket, SOMAXCONN);
	if (iResult == SOCKET_ERROR) { this->serverStop(this, ListenSocket, "[startListener] ERROR: Listen stage failed"); }
    dprintf("[startListener] SUCCESS: socket is now listening for incoming connexions.");

	// Accept a client socket
	this->socket = accept(ListenSocket, NULL, NULL);
	if (this->socket == INVALID_SOCKET) { this->serverStop(this, this->socket, "[startListener] ERROR: Accept stage failed"); }
    dprintf("[startListener] SUCCESS: socket accept stage successful.");

	// No longer need server socket
	closesocket(ListenSocket);
	return;
}




/**
	In case of failure, close properly the server:
		- Shutdown this->socket
		- Close this->socket
		- Invoke the class destructor

	@param		Server* this			Address of the instantiated object
	@param		SOCKET	Socket			Socket to close. If -1, then set it to this->socket
	@param		char*	error_message	Message to be displayed
 */
static void SocketError(Server* this, SOCKET Socket, char* error_message)
{
	if (Socket == -1) { Socket = this->socket; }
	dprintf("[SocketError] SOCKET ERROR: WSAGetLastError: %d", WSAGetLastError());
	dprintf("%s", error_message);
	shutdown(Socket, SD_SEND);
	closesocket(Socket);
	WSACleanup();
	this->destruct(this);
	exit(-1);
}



/**
	Print network packets received or to be send for debug purposes in tcpdump style.
	This function is only for debug purpose. If DEBUGTRACE preprocessor constant is
	not set, this function returns immediately without doing anything.
 */
void hexDump_if_debug_env(char* desc, void* addr, int len)
{
#ifdef DEBUGTRACE
	int i;
	int written_chars = 0;
	unsigned char buff[17];
	unsigned char temporary_buff[20] = { 0 };
	unsigned char line[74] = { 0 };
	unsigned char* pc = (unsigned char*)addr;

	// Output description if given.
	if (desc != NULL)
		dprintf("[hexDump] %s:\n", desc);

	if (len == 0)
	{
		dprintf("[hexDump] ERROR: data are zero length.");
		return;
	}
	if (len < 0)
	{
		dprintf("[hexDump] ERROR: data are negative length.: %i", len);
		return;
	}

	dprintf("[hexDump] Hexdump of packet:");

	// Process every byte in the data.
	for (i = 0; i < len; i++)
	{
		// Multiple of 16 means new line (with line offset).
		if ((i % 16) == 0)
		{
			// Just don't print ASCII for the zeroth line.
			if (i != 0)
			{
				strcat_s(line, 74, "  ");
				strcat_s(line, 74, buff);
				dprintf("[hexDump] %s", line);
				ZeroMemory(line, 74);
			}

			// Put the offset in line buffer.
			written_chars = sprintf_s(temporary_buff, 20, "  %04x ", i);
			strcat_s(line, 74, temporary_buff);
			ZeroMemory(temporary_buff, 20);
		}

		// Now the hex code for the specific character.
		written_chars = sprintf_s(temporary_buff, 20, " %02x", pc[i]);
		strcat_s(line, 74, temporary_buff);
		ZeroMemory(temporary_buff, 20);

		// And store a printable ASCII character for later.
		if ((pc[i] < 0x20) || (pc[i] > 0x7e))
			buff[i % 16] = '.';
		else
			buff[i % 16] = pc[i];
		buff[(i % 16) + 1] = '\0';
	}

	// Pad out last line if not exactly 16 characters.
	while ((i % 16) != 0)
	{
		written_chars = sprintf_s(temporary_buff, 20, "   ");
		strcat_s(line, 74, temporary_buff);
		ZeroMemory(temporary_buff, 20);
		i++;
	}

	// And print the final ASCII bit.
	written_chars = sprintf_s(temporary_buff, 20, "  %s\n", buff);
	strcat_s(line, 74, temporary_buff);
	ZeroMemory(temporary_buff, 20);
	dprintf("[hexDump] %s", line);
#endif

	return;
}
